package edu.ivanov.apm.chaos;

import java.util.Stack;

/**
 * Интерпретатор правил рисования фракталов.
 *
 * User: Илья
 * Date: 09.10.13
 * Time: 22:36
 */
public class Interpreter {

	private Turtle turtle;
	private double f;
	private double theta;

	private Stack<Position> stack = new Stack<Position>();

	/**
	 * @param turtle Черепашка для рисования.
	 */
	public void setTurtle(Turtle turtle) {
		this.turtle = turtle;
	}

	/**
	 * @param f Величина смещения при интерпретации смещения.
	 */
	public void setF(double f) {
		if (f < 0)
			throw new RuntimeException("Невозможно утановить величину смещения");

		this.f = f;
	}

	/**
	 * @param theta Модуль угла поворота при интерпретации поворота.
	 */
	public void setTheta(double theta) {
		if (theta < 0)
			throw new RuntimeException("Невозможно установить величину модуля угла поворота");

		this.theta = theta;
	}

	/**
	 * Запускает процесс интерпретации правила - рисования фрактала.
	 *
	 * @param rule Правило для интерпретации.
	 * @return Границы перемещений черепашки.
	 */
	public MotionBounds interpret(String rule) {
		if (rule == null || turtle == null)
			return null;

		Position initial = turtle.getPosition();

		MotionBounds bounds = new MotionBounds();
		bounds.setInitial(initial);

		double minX = initial.getX();
		double maxX = initial.getX();
		double minY = initial.getY();
		double maxY = initial.getY();

		StringBuilder components = new StringBuilder(rule);
		int length = components.length();
		for (int i = 0; i < length; i++) {
			char action = components.charAt(i);

			boolean updateBounds = false;
			switch (action) {
				case '+':
					turtle.rotate(theta);
					break;
				case '-':
					turtle.rotate(- theta);
					break;
				case '[':
					stack.push(turtle.getPosition());
					break;
				case ']':
					if (stack.isEmpty())
						throw new RuntimeException("Требуемое сохранённое состояние отсутствует");

					turtle.setPosition(stack.pop());
					break;
				case 'b':
					turtle.jump(f);
					updateBounds = true;
					break;
				default:
					turtle.go(f);
					updateBounds = true;
			}

			if (updateBounds) {
				Position position = turtle.getPosition();
				minX = Math.min(minX, position.getX());
				maxX = Math.max(maxX, position.getX());
				minY = Math.min(minY, position.getY());
				maxY = Math.max(maxY, position.getY());
			}
		}

		bounds.setTopLeft(new Position(minX, minY, 0));
		bounds.setTopRight(new Position(maxX, minY, 0));
		bounds.setBottomLeft(new Position(minX, maxY, 0));
		bounds.setBottomRight(new Position(maxX, maxY, 0));

		return bounds;
	}
}
